//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <fstream>

#include "libraries.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
struct SmilesRef {

  char* smiles;
  char* ref;

  SmilesRef () {
    
    this->smiles = NULL;
    this->ref    = NULL;
    
  }

  SmilesRef (char* smilesIn) {
    
    if (this->smiles) delete[] this->smiles;
    this->smiles = new char[strlen(smilesIn) + 1];
    strcpy(this->smiles, smilesIn);

    this->ref = NULL;
    
  }

  SmilesRef (char* smilesIn, char* refIn) {
    
    if (this->smiles) delete[] this->smiles;
    this->smiles = new char[strlen(smilesIn) + 1];
    strcpy(this->smiles, smilesIn);

    if (this->ref) delete[] this->ref;
    this->ref = new char[strlen(refIn) + 1];
    strcpy(this->ref, refIn);
    
  }

  ~SmilesRef () {
    
    if (smiles) delete[] smiles;
    if (ref)    delete[] ref;
    
  }

  struct SmilesRef operator= (const struct SmilesRef &smilesRef) {

    if (this->smiles) delete[] this->smiles;
    this->smiles = new char[strlen(smilesRef.smiles) + 1];
    strcpy(this->smiles, smilesRef.smiles);

    if (this->ref) delete[] this->ref;
    this->ref = new char[strlen(smilesRef.ref) + 1];
    strcpy(this->ref, smilesRef.ref);

    return *this;

  };

};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LINGOLENGTH  4
#define LENGTHARRAY  4096

#define SMILESRef    struct SmilesRef
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double obtainLingoSim (char*, char*);
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double obtainLingoSim (char* lingoString1, char* lingoString2) {

  if (strlen(lingoString1) < LINGOLENGTH || strlen(lingoString2) < LINGOLENGTH) return 0;

  int  i, j, k, z;
  int  length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  
  char tmpLingo1[length1], tmpLingo2[length2];

  //Change the Cl bu L and Br by R in the two lingo sequences
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    
    if (i < length1) {
      if      ((lingoString1[i] == 'l') || (lingoString1[i] == 'r')) tmpLingo1[j - 1] = (lingoString1[i] == 'l')?'L':'R';
      //else if (isdigit(lingoString1[i]))                             tmpLingo1[j++]   = '0';
      else                                                           tmpLingo1[j++]   = lingoString1[i];
    }
    
    if (i < length2) {
      if      ((lingoString2[i] == 'l') || (lingoString2[i] == 'r')) tmpLingo2[k - 1] = (lingoString2[i] == 'l')?'L':'R';
      //else if (isdigit(lingoString2[i]))                             tmpLingo2[k++]   = '0';
      else                                                           tmpLingo2[k++]   = lingoString2[i];
    }
    
  }
  
  tmpLingo1[j] = tmpLingo2[k] = '\0';

  /*j -= (LINGOLENGTH - 1); k -= (LINGOLENGTH - 1);*/
  j = length1 - (LINGOLENGTH - 1); k = length2 - (LINGOLENGTH - 1);
  
  int    L = j + k; //Total Lingos in two sequences
  double similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {

      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {

      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1.0 - ((double)(Nai - Nbi)/(double)(Nai + Nbi))):(1 - ((double)(Nbi - Nai)/(double)(Nai + Nbi))));
    else                 L--;
  }

  return (double)((double)similarityValue/(double)L);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main (int argc, char** argv) {
  
  String* namefileTargetSmilesFile = NULL;
  String* namefileDBSmilesFile     = NULL;

  if (argc < 4) {
    
    cout << "Error in the input parameters! Must by like: " << endl;
    cout << "./LINGOsim <Target Smiles File> < <DB Smiles File>" << endl;
    
    return EXIT_FAILURE;
    
  }
  else {
    
    namefileTargetSmilesFile = new String(argv[1]);
    namefileDBSmilesFile     = new String(argv[3]);
    /*
    cout << "---------------------------------------------------------------------------------" << endl;
    cout << " Target Smiles File : " << argv[1] << endl;
    cout << "     DB Smiles File : " << argv[3] << endl;
    cout << "---------------------------------------------------------------------------------" << endl;
    */
  }
  
  fstream fileTarget, fileDB;
  
  fileTarget.open(namefileTargetSmilesFile->Out(), fstream::in);
  
  if (!fileTarget.is_open()) {
    
    if (namefileTargetSmilesFile) delete namefileTargetSmilesFile;
    if (namefileDBSmilesFile)     delete namefileDBSmilesFile;
    
    return EXIT_FAILURE;    
    
  }
  
  fileDB.open(namefileDBSmilesFile->Out(), fstream::in);
  
  if (!fileDB.is_open()) {
    
    fileTarget.close();
    
    if (namefileTargetSmilesFile) delete namefileTargetSmilesFile;
    if (namefileDBSmilesFile)     delete namefileDBSmilesFile;
    
    return EXIT_FAILURE;    
    
  }
  
  TListE <SMILESRef>* listTarget = new TListE <SMILESRef> ();
  
  char                buffer [LENGTHARRAY + 1], *smileRead, *refRead;
  
  while (!fileTarget.eof()) {
    
    fileTarget.getline(buffer, LENGTHARRAY);
    if (!strlen(buffer)) continue;
    
    if ((smileRead = strtok(buffer, " \t\n\r"))) 
      listTarget->Add(new SMILESRef (smileRead));
    
  }
  
  while (!fileDB.eof()) {
    
    fileDB.getline(buffer, LENGTHARRAY);
    
    if ((!strlen(buffer))                          ||
        (!(smileRead = strtok(buffer, " \t\n\r"))) ||
        (!(refRead = strtok(NULL, " \t\n\r"))))     
      continue;
    
    printf("%s %s", smileRead, refRead);
    
    listTarget->SetInitial();
    while (listTarget->SetNext()) 
      printf(" %.6f", obtainLingoSim(listTarget->GetCurrent()->smiles, smileRead));
    
    printf("\n");
     
  }
  
  fileDB.close();
  
  if (listTarget)               delete listTarget;
  
  if (namefileTargetSmilesFile) delete namefileTargetSmilesFile;
  if (namefileDBSmilesFile)     delete namefileDBSmilesFile;
  
  
  return EXIT_SUCCESS;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
